﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web.UI;
using Ext.Net;
using Ext.Net.Utilities;

namespace KamLib
{


    public interface IFormLogicContainer : IEquipContainer
    {
        FormLogic Logic { get; set; }
        void Initialize();
    }

    public class UiFormFieldInfo
    {
        public string Name;
        public Type Type;

        public int MaxLength;
        public bool Nullable;
    }

    public abstract class FormLogic : UilNode
    {
        public bool CanReload = true;
        public bool CanSave = true;

        /// <summary>
        /// ClientID зависимых Stores, которых надо обновлять при изменении текущих данных
        /// </summary>

        public string FormWindowID = "Wnd";
        public string FormID = "Form";
        public bool Creating = true;

        //public abstract FormDataBase LoadData();
        public abstract List<UiFormFieldInfo> FieldInfos { get; }

        public abstract object GetData();
        public abstract void SaveData();
        public abstract void DeleteData();
        public abstract Control GetForm();

        public string Title;
        public abstract string JValues { get; set; }

        protected virtual void PreBeforeInitControl(Control a) { }

        protected virtual void BeforeInitControl() { }

        public virtual Control CreateControl(RenderMode renderMode = RenderMode.RenderTo, string renderElement = "form1")
        {
            Creating = true;
            return Response.Create<WindowFormLogicControl>(
                Controller.VirtualPath + LIID + ".ascx", ControlID,
                a =>
                {
                    a.Logic = this;
                    PreBeforeInitControl(a);
                    BeforeInitControl();
                    a.Initialize();
                    return a._Wnd;
                },
                renderMode, renderElement
            );
        }

        public virtual Control ModifyControl()
        {
            Creating = false;
            return Response.Modify<WindowFormLogicControl>(
                Controller.VirtualPath + LIID + ".ascx", Key.CID,
                a =>
                {
                    a.Logic = this;
                    PreBeforeInitControl(a);
                    BeforeInitControl();
                    a.Initialize();
                    return a._Wnd;
                }
            );
        }

        public abstract void SetValues(object values);


        #region AddDetail
        /// <summary>
        /// 
        /// </summary>
        /// <param name="logic"></param>
        /// <param name="getGrid"></param>
        /// <param name="jsonListField">Для случая, когда изменения detail-списока на форме должен запоминаться одновременно с формой.</param>
        /// <returns></returns>
        public UilNodeDetail AddDetail(ListLogic logic, Func<Control, GridPanel> getGrid, string jsonListField = null)
        {
            if (logic == null)
                throw new ArgumentNullException("logic");
            if (getGrid == null)
                throw new ArgumentNullException("getGrid");

            var detail = AddDetail(logic, (d, c) =>
                GridLogicAdapter.Apply(c, (ListLogic)d.Logic, getGrid(c))
            );

            detail.UseMasterForm = true;

            if (jsonListField.IsNotEmpty())
            {
                detail.JsonListField = jsonListField;
                logic.IsLocal = true;
                detail.MasterPreNew = detail.MasterPreLoad = (m, d) =>
                {
                    var form = GetForm();
                    if (form == null) return;
                    var grid = getGrid(form);
                    detail.Control = grid;
                    grid.CustomConfig.Add(new ConfigItem("disableAutoLoad", "1"));
                };
            }
            else
            {
                detail.MasterPreNew = detail.MasterPreLoad = (m, d) =>
                {
                    var form = GetForm();
                    if (form == null) return;
                    var isnew = m.Key.IsNew;
                    var grid = getGrid(form);
                    detail.Control = grid;
                    grid.Disabled = isnew;
                    if (isnew)
                        grid.CustomConfig.Add(new ConfigItem("disableAutoLoad", "1"));
                };
            }
            return detail;
        }

        #endregion


        #region Prepare Values

        protected virtual string GetName()
        {
            return "#" + Key.SimpleID;
        }

        protected virtual string GetTitle()
        {
            return Key.IsNew ? "New " + EntityDesc.Name : "Edit " + EntityDesc.Name + " " + GetName();
        }

        protected virtual void BeforePreNewValues()
        {
            if (GetForm() != null || Response == null) return;

            if (Details.UseMasterForm())
                ModifyControl();
            else
            {
                var preNew = GetType().GetMethod("PreNewValues");
                var attrs = preNew.GetCustomAttributes(typeof(UseFormAttribute), true);
                if (attrs.Length > 0) ModifyControl();
            }
        }

        /// <summary>
        /// Для подготовки values после создания новой.
        /// </summary>
        public virtual void PreNewValues() { }
        protected virtual void AfterPreNewValues()
        {
            Title = GetTitle();
            Details.MasterPreNew(this);
        }

        protected virtual void BeforePreLoadValues()
        {
            if (GetForm() != null || Response == null) return;

            if (Details.UseMasterForm())
                ModifyControl();
            else
            {
                var preLoad = GetType().GetMethod("PreLoadValues");
                var attrs = preLoad.GetCustomAttributes(typeof(UseFormAttribute), true);
                if (attrs.Length > 0) ModifyControl();
            }
        }

        /// <summary>
        /// Для подготовки values после чтения.
        /// </summary>
        public virtual void PreLoadValues() { }
        protected virtual void AfterPreLoadValues()
        {
            Title = GetTitle();
            Details.MasterPreLoad(this);
        }

        public virtual void PreSaveValues() { }
        protected virtual void AfterPreSaveValues()
        {
            Title = GetTitle();
            Details.MasterPreSave(this);
        }

        #endregion
    }


    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public class UseFormAttribute : Attribute { }

    public abstract class FormLogic<TFormControl> : FormLogic
        where TFormControl : WindowFormLogicControl
    {
        public TFormControl Form;

        public override Control GetForm() { return Form; }

        #region AddDetail
        /// <summary>
        /// 
        /// </summary>
        /// <param name="logic"></param>
        /// <param name="getGrid"></param>
        /// <param name="jsonListField">Для случая, когда изменения detail-списока на форме должен запоминаться одновременно с формой.</param>
        /// <returns></returns>
        public UilNodeDetail AddDetail(ListLogic logic, Func<TFormControl, GridPanel> getGrid, string jsonListField = null)
        {
            return base.AddDetail(logic, c => getGrid(c as TFormControl), jsonListField);
        }

        public UilNodeDetail AddDetail(string liid, Func<TFormControl, GridPanel> getGrid, string jsonListField = null)
        {
            return AddDetail(GetDetailLogic(null, liid) as ListLogic, getGrid, jsonListField);
        }

        public UilNodeDetail AddDetail(string lid, string liid, Func<TFormControl, GridPanel> getGrid, string jsonListField = null)
        {
            return AddDetail(GetDetailLogic(lid, liid) as ListLogic, getGrid, jsonListField);
        }

        #endregion


        protected override void PreBeforeInitControl(Control a)
        {
            Form = (TFormControl)a;
        }
        public override void SetValues(object values)
        {
            Form._Wnd.Title = Title;
            Form._Form.AddScript(
                "KamLib.Logic.Form.setValues(" + Form._Form.ClientID + "._biLItem, " + JSON.Serialize(values) + ");"
            );
        }
    }


    public class ToolFormLogic<TFormControl> : FormLogic<TFormControl>
        where TFormControl : WindowFormLogicControl
    {
        public override List<UiFormFieldInfo> FieldInfos { get { return null; } }
        public override object GetData()
        {
            return new[] { new { ID = 0 } };
        }
        public override void SaveData() { }
        public override void DeleteData() { }
        public override string JValues { get { return null; } set { } }
        public override object GetResyncData(UilResyncKey key) { return null; }

        public override void AfterInit()
        {
            base.AfterInit();
            CanReload = false;
            CanSave = false;
        }
    }

    public class ToolFormLogic : ToolFormLogic<WindowFormLogicControl> { }


    public abstract class FormLogic<TFormControl, TFormData> : FormLogic<TFormControl>
        where TFormControl : WindowFormLogicControl
        where TFormData : class, new()
    {
        protected static readonly Type _dataType = typeof(TFormData);
        public Type DataType { get { return _dataType; } }

        public TFormData Values;

        public override void InitCheck()
        {
            if (KeyFields == null)
                KeyFields = GetKeyFields();
            base.InitCheck();
        }

        protected static string[] DefaultKeyFields;
        public static string[] GetKeyFields()
        {
            if (DefaultKeyFields == null)
            {
                var m = typeof(TFormData).GetMembers().FirstOrDefault(
                    a => a.MemberType == MemberTypes.Field || a.MemberType == MemberTypes.Property);

                if (m == null)
                    throw new Exception("Can't get KeyFields. The type parameter TFormData '" + typeof(TFormData).Name +
                        "' is not containts fields or properties.");

                DefaultKeyFields = new[] { m.Name };
            }

            return DefaultKeyFields;
        }


        private static List<UiFormFieldInfo> _FieldInfos;
        //public override List<UiFormFieldInfo> FieldInfos { get { return (_FieldInfos = CreateFieldInfos()); } } //!!!
        public override List<UiFormFieldInfo> FieldInfos { get { return _FieldInfos ?? (_FieldInfos = CreateFieldInfos()); } }

        protected virtual List<UiFormFieldInfo> CreateFieldInfos()
        {
            var infos = new List<UiFormFieldInfo>();

            foreach (var member in typeof(TFormData).GetPropertiesAndFields())
            {
                infos.Add(new UiFormFieldInfo { Name = member.Name, Type = member.ResultType(), });
            }

            return infos;
        }

        #region Select

        protected abstract TFormData Select();

        public override object GetData()
        {
            //var log = Controller.Context.Log;

            //log += "GetData()";
            Activate();
            //log &= "Activate";
            Values = Key.IsNew ? new TFormData() : Select();
            //log &= "Select";
            if (Values == null)
                throw new ArgumentException(GetType().Name +" by "+ Key + ": Not exists any entities with this key.");

            if (Key.IsNew)
            {
                BeforePreNewValues();
                PreNewValues();
                AfterPreNewValues();
            }
            else
            {
                BeforePreLoadValues();
                //log &= "BeforePreLoadValues";
                PreLoadValues();
                //log &= "PreLoadValues";
                AfterPreLoadValues();
                //log &= "AfterPreLoadValues";
            }
            //log--;

            return Values;
        }

        protected void SelectLocalDetails(TFormData values)
        {
            foreach (var d in Details)
            {
                if (d.JsonListField.IsEmpty()) continue;
                var dlogic = d.Logic as ListLogic;
                if (dlogic == null || !dlogic.IsLocal) continue;

                var m = typeof(TFormData).GetPropertyOrField(d.JsonListField);
                if (m == null) continue;
                var mtype = m.ResultType();
                dlogic.Assign(this);
                m.SetValue(values, mtype.IsArray ? dlogic.SelectArray() : dlogic.Select(), null);
            }
        }

        #endregion

        #region Save

        protected virtual void SaveEntities()
        {
            throw new NotImplementedException();
        }

        public override void SaveData()
        {
            //if (Key.IsNew && !Node.CanInsert)
            //    throw new Exception("Not have rigth to insert into the table.");
            //if (!Key.IsNew && !Node.CanEdit)
            //    throw new Exception("Not have rigth to update the table.");

            Activate();
            SaveEntities();

            if (Key != null && Details != null)
                Details.LoadMasterID(Key);
        }

        public void SaveLocalChanges()
        {
            foreach (var detail in Details)
            {
                if (detail.JsonListField.IsEmpty() && detail.Logic is ListLogic) continue;
                var llogic = (ListLogic)detail.Logic;
                if (!llogic.IsLocal) continue;
                llogic.Assign(this);
                llogic.SaveLocalChanges(
                    (IEnumerable<object>)typeof(TFormData).GetValue(Values, detail.JsonListField, null)
                );
            }
        }
        public override object GetResyncData(UilResyncKey key)
        {
            if (key.Mode == UilResyncMode.Delete)
                return key.Key != null && key.Key.ID != null ? new { delId = key.Key.ID.First().Value } : null;

            Values = Values ?? Select();

            if (Values != null)
            {
                BeforePreLoadValues();
                PreLoadValues();
                AfterPreLoadValues();
                Response += ControlID + "_Wnd.setTitle('" + Title + "');";
            }

            return Values;
        }



        public override string JValues
        {
            get { return JSON.Serialize(Values); }
            set { Values = DeserializeValues(value); }
        }

        protected TFormData DeserializeValues(string jvalues)
        {
            var converters = JSON.Converters;
            converters.Add(new CheckboxJsonConverter());
            return JSON.Deserialize<TFormData>(jvalues, converters);
        }

        #endregion

        #region Delete

        public override void DeleteData()
        {
            if (Key.IsNew)
                throw new Exception("Can't delete new entity.");
            Activate();
            DeleteEntity();
        }

        public virtual void DeleteEntity() { }


        #endregion

        public override void SetValues(object values)
        {
            Values = (TFormData)values;
            base.SetValues(values);
        }
    }

}